<html>
<head>
<title>DrawMe - A network ink-chat application exploring .NET 3.5, WPF and WCF</title>
</head>

<body>

<ul class="download">
<li><a href="DrawMe_demo.zip">Download DrawMe demo application - 32 Kb</a></li>
<li><a href="DrawMe_src.zip">Download DrawMe source (VS2008 Beta 2) - 48 Kb</a></li>
</ul>

<p><img src="DrawMe1.jpg" alt="DrawMe sample image" width="500" height="328" /></p>

<h2>Table of Contents</h2>
<ul>
<li><a href="#intro">Introduction</a>
<ul>
<li><a href="#demouse">Using the Demo</a></li>
</ul>
</li>
<li><a href="#buildui">Building the user interface using WPF</a>
<ul>
<li><a href="#loginwin">The login control</a></li>
<li><a href="#mainwin">The main application window</a></li>
</ul>
</li>
<li><a href="#sequence">DrawMe Sequence Flow</a>
<ul>
<li><a href="#loginseq">Login</a></li>
<li><a href="#drawingseq">Process Ink Strokes</a></li>
<li><a href="#logoutseq">LogOff</a></li>
</ul>
</li>
<li><a href="#commun">Communicating using WCF</a>
<ul>
<li><a href="#serial">Serializing objects</a></li>
<li><a href="#contract">Service contract</a></li>
<li><a href="#callbacks">Client callbacks</a></li>
</ul>
</li>
<li><a href="#conclusion">Conclusion</a></li>
<li><a href="#appendix">Appendix - Collaborating via CodePlex</a></li>
</ul>

<h2><a name="intro">Introduction</a> </h2>
<p>This demo project came about as a result of brainstorming on ideas for making an article to enter into the VS2008 contest! 
We wanted to experiment with and showcase a few of the great new .NET 3.0 (and 3.5) technologies that have been introduced with the lastest version of Visual Studio. 
Initially we came up with the concept of a network chat program - we were going to implement the GUI with WPF and the network communication with WCF. 
After some experimenting around with the new WPF controls we decided it would be more fun to use the new <code>InkCanvas</code> control and make a multi-user network drawing demo.
DrawMe is the result of our experiments and in this article we'll walk through some of the interesting WPF and WCF code features we encountered along the way.   
</p>

<p>At the highest level, DrawMe uses a client-server architecture with the server hosted on one of the client's computers. 
When a user starts DrawMe they are given the choice of creating a new server to locally host ink drawings, or connecting to an existing DrawMe server which can be either local or remote. 
When a user draws on the ink canvas, the drawing strokes are broadcast to all clients registered with the main DrawMe server. 
In this way users can participate in real-time collaborative drawing. Although this concept isn't new, the goal of this article was to see how easy this would be to implement using WPF and WCF.  
</p>

<h3><a name="demouse">Using the Demo</a></h3>
<p>If you just want to try out the finished product you can download the demo application using the link at the top of the article. 
Chances are high that most people will just be testing it out on one computer, in which case it should work with minimal firewall configuration required. 
Simply start up a couple of instances of the DrawMe.exe and set the first one to be the server. 
When connecting the second instance change the type to Client and specify either localhost, the machine name, or the machine IP address for the server address. 
If you want to try it out on two or more separate computers on a LAN you will likely need to allow DrawMe access through any firewalls you have running. 
If you want to try it out on two or more computers across the internet you will also probably need to port-forward TCP 8000 to your computer from any router you are using etc. 
We've successfully tested it in all of the scenarios listed here so hopefully it should work for you too!
</p>

<h2><a name="buildui">Building the user interface using WPF</a></h2>
<p>The user interface for DrawMe consists of two main windows:
</p>
<ul>
<li>The login control - A WPF user control where you can create and/or connect to a DrawMe server</li>
<li>The main application window - Where all ink drawing takes place</li>
</ul>
<p>
The following sub-sections explore the function and creation of each of these areas of the user interface.
</p>

<h3><a name="loginwin">The login control</a></h3>
<p>When the user first starts DrawMe they are presented with a login screen. 
The purpose of the login screen is to allow the user to either join an existing DrawMe session or to create a new DrawMe server and also join that session as the first client.
The following screenshot shows how the login window looks.
</p>
<p><img src="DrawMe2.jpg" alt="The login screen" width="353" height="303" /></p>

<p>It's a relatively basic UI, but it doesn't need to do much so we've kept it simple. 
There's a few nice features that WPF brings to the table that we should point out:
</p>
<ul>
<li>It's easy to make visually pleasing rounded corners on the rectangular regions of the UI by specifying the <code>CornerRadius</code> property for the element</li>
<li>It's easy to specify nice colour gradients by applying a <code>LinearGradientBrush</code> to the background of elements</li>
</ul>

<p>The following xaml code listing shows how we made the login control. 
We found that working with the raw xaml was the fastest way to experiement and fine tune the design; 
however, the layout design manager in VS2008 also does a nice job if you want to play around with the controls.
</p>

<pre lang="cs">&lt;UserControl x:Class="DrawMe.LoginControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="350" Loaded="UserControl_Loaded"&gt;
    &lt;StackPanel&gt;
        &lt;Border Height="50" BorderBrush="#FFFFFFFF" Background="Black" BorderThickness ="2,2,2,0" CornerRadius="5,5,0,0"&gt;
                &lt;Label Content="Welcome to DrawMe" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="20" Foreground="White"/&gt;
        &lt;/Border&gt;
        &lt;Border Height="220" BorderBrush="#FFFFFFFF" BorderThickness="2,2,2,0" CornerRadius="5,5,0,0"&gt;
            &lt;Border.Background&gt;
                &lt;LinearGradientBrush EndPoint="0.713,0.698" StartPoint="0.713,-0.139"&gt;
                    &lt;GradientStop Color="#FFFFFFFF" Offset="0.933"/&gt;
                    &lt;GradientStop Color="LightBlue" Offset="0.337"/&gt;
                &lt;/LinearGradientBrush&gt;
            &lt;/Border.Background&gt;
            &lt;StackPanel Name="infoPanel" Orientation="Vertical" Margin="10,10,10,10"&gt;
                &lt;StackPanel Name="typePanel" Orientation="Horizontal"&gt;
                    &lt;Label Name="lblChatType" FontSize="20" Width="120" HorizontalContentAlignment="Right" VerticalContentAlignment="Center"&gt;Type:&lt;/Label&gt;
                    &lt;RadioButton Name="chatTypeServer" FontSize="20" VerticalAlignment="Center" Margin="0,0,20,0" 
                                 Checked="chatTypeServer_Checked" VerticalContentAlignment="Center"&gt;Server&lt;/RadioButton&gt;
                    &lt;RadioButton Name="chatTypeClient" FontSize="20" VerticalAlignment="Center" 
                                 Checked="chatTypeClient_Checked" VerticalContentAlignment="Center"&gt;Client&lt;/RadioButton&gt;
                &lt;/StackPanel&gt;
                &lt;StackPanel Name="serverPanel" Orientation="Horizontal" Margin="0,10,0,0"&gt;
                    &lt;Label Name="lblServer" FontSize="20" Width="120" HorizontalContentAlignment="Right" VerticalContentAlignment="Center"&gt;Server:&lt;/Label&gt;
                    &lt;TextBox Height="30" Name="txtServer" Width="160" FontSize="20" VerticalContentAlignment="Center" /&gt;
                &lt;/StackPanel&gt;
                &lt;StackPanel Name="usernamePanel" Orientation="Horizontal" Margin="0,10,0,10"&gt;
                    &lt;Label Name="lblUserName" FontSize="20" Width="120" HorizontalContentAlignment="Right"&gt;User Name:&lt;/Label&gt;
                    &lt;TextBox Height="30" Name="txtUserName" Width="160" FontSize="20" VerticalContentAlignment="Center" /&gt;
                &lt;/StackPanel&gt;
                &lt;StackPanel Name="buttonPanel" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center"&gt;
                    &lt;Button Name="btnLogin" Width="120" FontSize="20" Margin="10,10,10,10" Click="btnLogin_Click"&gt;Connect&lt;/Button&gt;
                    &lt;Button Name="btnCancel" Width="120" FontSize="20" Margin="10,10,10,10" Click="btnCancel_Click"&gt;Cancel&lt;/Button&gt;
                &lt;/StackPanel&gt;
            &lt;/StackPanel&gt;
        &lt;/Border&gt;
        &lt;Border Height="30" Background="#FF2E2E2E" BorderBrush="#FFFFFFFF" BorderThickness="2,0,2,2" CornerRadius="0,0,5,5"&gt;
            &lt;Label Content="DrawMe is using .NET 3.5 (WPF and WCF)" FontSize="9" Foreground="#FFFFFFFF" 
                   HorizontalAlignment="Center" VerticalAlignment="Center" Background="#00FFFFFF"/&gt;
        &lt;/Border&gt;
    &lt;/StackPanel&gt;
&lt;/UserControl&gt;
</pre>

<h3><a name="mainwin">The main application window</a></h3>
<p>After the user has logged in to a DrawMe server the application enables the main DrawMe window where all drawing takes place. 
The window consists of four major sections: 
</p>
<ul>
<li>Information Bar - A <code>StackPanel</code> along the top of the window that displays the connection status (as a fade-in/fade-out animation) as welll as information about who has drawn most recently. There is also a sign out button for when a user wants to leave the session.</li>
<li>Client List - A <code>ListView</code> along the left of the window that displays user names for all connected clients.</li>
<li>Ink Tool Selection - A <code>StackPanel</code> below the Information bar that allows users to select how to interact with the ink canvas.</li>
<li>Ink Canvas - An <code>InkCanvas</code> control that handles the display of ink drawings from all connected clients.</li>
</ul>
<p>The following screenshot shows how the main application window looks.</p>
<p><img src="DrawMe3.jpg" alt="The main application window" width="600" height="450" /></p>

<p>Again, there's a few nice WPF features in use that we should point out:
</p>
<ul>
<li>It's possible to specify a <code>DropShadowBitmapEffect</code> for xaml elements - notice the drop-shadow around the Client List.</li>
<li>It's easy to animate text via the <code>DoubleAnimation</code> element - In the xaml code listing that follows you can see we've set the connection status animation to cycle between opaque and transparent every 5 seconds.</li>
<li>The <code>InkCanvas</code> control is ready to use without alteration - All we had to do was wire up some handlers for the events raised when a stroke is collected or erased. The different interaction modes (Ink, Erase By Stroke, Erase By Point) are standard included editing modes for the <code>InkCanvas</code> control.</li>
<li>Attributes on xaml elements can be bound to properties (<code>DependencyProperty</code>) in the underlying class - We store the current ink colour in the <code>FillColor</code> property, which is just a wrapper around a <code>DependencyProperty</code>. 
The interesting thing to note here is that when <code>FillColor</code> is updated programatically in the code-behind file, no extra effort is required to update the actual displayed colour in the GUI; the update happens automatically once the property has been bound correctly.</li>
</ul>

<p>When the user clicks on the colour button a colour picker dialog is displayed. Unfortunately,WPF has no native colour selection dialog.
Luckily, we found <a href="http://blogs.msdn.com/wpfsdk/archive/2006/10/26/Uncommon-Dialogs--Font-Chooser-and-Color-Picker-Dialogs.aspx">this</a> colour picker dialog on one of the MSDN blogs. 
We modified it slightly to be consistent with our colour scheme, but essentially it's been used as-is.
</p>

<p>The following xaml code listing shows how we made the main application window.
</p>

<pre lang="cs">&lt;Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="DrawMeMainWindow"
    x:Class="DrawMe.DrawMeWindow" 
    Title="DrawMeWindow" Height="600" Width="800"
    Background="#FF3B3737" Loaded="Window_Loaded" MinWidth="800" MinHeight="500"&gt;

    &lt;Grid x:Name="LayoutRoot" &gt;
        &lt;Grid.RowDefinitions&gt;
            &lt;RowDefinition Height="65" /&gt;
            &lt;RowDefinition Height="50" /&gt;
            &lt;RowDefinition Height="*" /&gt;
        &lt;/Grid.RowDefinitions&gt;
        &lt;Grid.ColumnDefinitions&gt;
            &lt;ColumnDefinition Width="150" /&gt;
            &lt;ColumnDefinition Width="*" /&gt;
        &lt;/Grid.ColumnDefinitions&gt;

        &lt;Border Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" BorderBrush="Gray" BorderThickness="1,1,1,1" CornerRadius="8,8,8,8"&gt;
            &lt;StackPanel Name="loginStackPanel" Orientation="Horizontal" HorizontalAlignment="Left"&gt;
                &lt;StackPanel Orientation="Vertical" Margin="10,10,20,0"&gt;
                    &lt;TextBlock Name="ApplicationTypeMessage" Width="120" Height="25" FontSize="10" Foreground="White" TextAlignment="Center"&gt;
                        Waiting for connection...
                        &lt;TextBlock.Triggers&gt;
                            &lt;EventTrigger RoutedEvent="TextBlock.Loaded"&gt;
                                &lt;BeginStoryboard&gt;
                                    &lt;Storyboard Name="ApplicationTypeMessageStoryBoard"&gt;
                                        &lt;DoubleAnimation  Name="ApplicationTypeMessageAnimation"
                                            Storyboard.TargetName="ApplicationTypeMessage" 
                                            Storyboard.TargetProperty="(TextBlock.Opacity)"
                                            From="1.0" To="0.0" Duration="0:0:5" 
                                            AutoReverse="True" RepeatBehavior="Forever" 
                                         /&gt;
                                    &lt;/Storyboard&gt;
                                &lt;/BeginStoryboard&gt;
                            &lt;/EventTrigger&gt;
                        &lt;/TextBlock.Triggers&gt;
                    &lt;/TextBlock&gt;
                    &lt;Button Name="btnLeave" Width="100" Height="20" FontSize="10" Click="btnLeave_Click"&gt;
                        Sign Out
                    &lt;/Button&gt;
                &lt;/StackPanel&gt;
                &lt;TextBlock Name="AnimatedMessage" FontSize="35" FontWeight="Bold" Foreground="White" VerticalAlignment="Center"&gt;
                    Welcome to DrawMe
                &lt;/TextBlock&gt;
            &lt;/StackPanel&gt;
        &lt;/Border&gt;

        &lt;Border Name="BorderUsersList" Grid.Column="0" Grid.Row="1" Grid.RowSpan="2" CornerRadius="8,8,8,8" Background="LightBlue" BorderThickness="4,4,4,4"&gt;
            &lt;ListView Name="lvUsers" Margin="10" FontSize="20"&gt;
                &lt;ListView.BitmapEffect&gt;
                    &lt;DropShadowBitmapEffect /&gt;
                &lt;/ListView.BitmapEffect&gt;
            &lt;/ListView&gt;
        &lt;/Border&gt;

        &lt;Border Name="BorderEditingType" Grid.Column="1" Grid.Row="1" CornerRadius="8,8,8,8" Background="LightBlue" BorderThickness="0,4,4,4"&gt;
                &lt;StackPanel Orientation="Horizontal" VerticalAlignment="Center"&gt;
                    &lt;RadioButton Name="rbInk" Content="Ink" Margin="15,0,0,0" VerticalAlignment="Center" FontSize="20" IsChecked="True" 
                                 Tag="{x:Static InkCanvasEditingMode.Ink}" Click="rbInkType_Checked"&gt;
                    &lt;/RadioButton&gt;
                    &lt;RadioButton Name="rbEraserByStroke" Content="Erase By Stroke" Margin="15,0,0,0" VerticalAlignment="Center" FontSize="20" 
                                 Tag="{x:Static InkCanvasEditingMode.EraseByStroke}" Click="rbInkType_Checked"&gt;
                    &lt;/RadioButton&gt;
                    &lt;RadioButton Name="rbEraserByPoint" Content="Erase By Point" Margin="15,0,0,0" VerticalAlignment="Center" FontSize="20" 
                                 Tag="{x:Static InkCanvasEditingMode.EraseByPoint}" Click="rbInkType_Checked"&gt;
                    &lt;/RadioButton&gt;
                    &lt;TextBlock Margin="25,0,10,0" VerticalAlignment="Center" FontSize="20" &gt;Colour:&lt;/TextBlock&gt;
                    &lt;Button Margin="0,0,0,0" Background="White" Height="28" Width="64" Click="OnSetFill"&gt;
                        &lt;Rectangle Width="54" Height="20" Stroke="Black" StrokeThickness="2"&gt;
                            &lt;Rectangle.Fill&gt;
                                &lt;SolidColorBrush Color="{Binding ElementName=DrawMeMainWindow, Path=FillColor}" /&gt;
                            &lt;/Rectangle.Fill&gt;
                        &lt;/Rectangle&gt;
                    &lt;/Button&gt;
            &lt;/StackPanel&gt;
        &lt;/Border&gt;

        &lt;Border Name="BorderInkCanvas" Grid.Column="1" Grid.Row="2" Background="LightBlue" BorderThickness="0,0,4,4" CornerRadius="8,8,8,8" &gt;
            &lt;InkCanvas x:Name="inkCanv" Margin="10" Background="White" 
                        StrokeCollected="inkCanv_StrokeCollected" StrokeErasing="inkCanv_StrokeErasing" 
                       StrokeErased="inkCanv_StrokeErased"&gt;
            &lt;/InkCanvas&gt;
        &lt;/Border&gt;

        &lt;Canvas Name="loginCanvas" Grid.Column="1" Grid.Row="2" Width="500" Height="300" VerticalAlignment="Top" HorizontalAlignment="Center" /&gt;
    &lt;/Grid&gt;
&lt;/Window&gt;
</pre>

<h2><a name="sequence">DrawMe Sequence Flow</a></h2>
<p>
To help explain the main runtime scenarios when using DrawMe, we have constructed some UML sequence diagrams to represent the state of the application at various stages. 
</p>

<h3><a name="loginseq">Login</a></h3>
<p>During login up to four main events can occur:
</p>
<ul>
<li>Start Server - If the user is starting a new DrawMe server the application spawns a thread to run the DrawMeService which will coordinate communication between clients. We've used TCP for the transport protocol but WCF allows for the protocol to be easily changed</li>
<li>Start Client -  Construct a <code>ClientCallBack</code> (implements <code>IDrawMeServiceCallback</code>) to provide a means for the server to invoke functions on the client. Also construct a <code>DrawMeServiceClient</code> to handle TCP communication with the DrawMe server, and use this to connect to the server</li>
<li>Update Client List - The server utilises the client's callback to update the list of users registered with the server</li>
<li>Finalise Login - Close the login control and begin ink-chat mode</li>
</ul>
<p><img src="DrawMe4.jpg" alt="The Login Process" width="1024" height="760" /></p>
<p>The following code listing shows how we've implemented the login process. Note that for the sake of simplicity we've disabled all security (see <code>App.config</code>). We've also hard-coded the communication port to 8000. 
Again, this is just to make things easier for the sake of the demo. In a real-world application we probably wouldn't do this!&nbsp;</p>
    <p>
        App.config
    </p>
    <pre lang="cs">&lt;bindings&gt; <br />	&lt;netTcpBinding&gt;
		&lt;binding name="DrawMeNetTcpBinding"&gt;
			&lt;security mode="None"&gt; 
				&lt;transport clientCredentialType="None" /&gt; 
				&lt;message clientCredentialType="None" /&gt; 
			&lt;/security&gt; 
		&lt;/binding&gt; 
	&lt;/netTcpBinding&gt;
&lt;/bindings&gt;    
</pre>
<br />
LoginControl.xaml.cs
<pre lang="cs">private void btnLogin_Click(object sender, RoutedEventArgs e)
{
    EndpointAddress serverAddress;
    if (this.chatTypeServer.IsChecked == true)
    {
        DrawMe.App.s_IsServer = true;
        serverAddress = new EndpointAddress("net.tcp://localhost:8000/DrawMeService/service");
    }
    else
    {
        DrawMe.App.StopServer();
        DrawMe.App.s_IsServer = false;
        if (txtServer.Text.Length == 0)
        {
            MessageBox.Show("Please enter server name");
            return;
        }
        serverAddress = new EndpointAddress(string.Format("net.tcp://{0}:8000/DrawMeService/service", txtServer.Text));
    }

    if (txtUserName.Text.Length == 0)
    {
        MessageBox.Show("Please enter username");
        return;
    }

    if (DrawMeServiceClient.Instance == null)
    {
        if (App.s_IsServer)
        {
            DrawMe.App.StartServer();
        }

        try
        {
            ClientCallBack.Instance = new ClientCallBack(SynchronizationContext.Current, m_mainWindow);
            DrawMeServiceClient.Instance = new DrawMeServiceClient
                                            (
                                                new DrawMeObjects.ChatUser
                                                (
                                                    txtUserName.Text,
                                                    System.Environment.UserName,
                                                    System.Environment.MachineName,
                                                    System.Diagnostics.Process.GetCurrentProcess().Id,
                                                    App.s_IsServer
                                                ),
                                                new InstanceContext(ClientCallBack.Instance),
                                                "DrawMeClientTcpBinding",
                                                serverAddress
                                            );
            DrawMeServiceClient.Instance.Open();
        }
        catch (System.Exception ex)
        {
            DrawMe.App.StopServer();
            DrawMeServiceClient.Instance = null;
            MessageBox.Show(string.Format("Failed to connect to chat server, {0}", ex.Message),this.m_mainWindow.Title);
            return;
        }
    }

    if (DrawMeServiceClient.Instance.IsUserNameTaken(DrawMeServiceClient.Instance.ChatUser.NickName))
    {
        DrawMeServiceClient.Instance = null;
        MessageBox.Show("Username is already in use");
        return;
    }

    if (DrawMeServiceClient.Instance.Join() == false)
    {
        MessageBox.Show("Failed to join chat room");
        DrawMeServiceClient.Instance = null;
        DrawMe.App.StopServer();
        return;
    }

    this.m_mainWindow.ChatMode();
}</pre>

<h3><a name="drawingseq">Process Ink Strokes</a></h3>
<p>
Once the client has connected to a server the application is ready to send and receive inkstrokes. The two main events that can happen at this stage are:
</p>
<ul>
<li>SendInkStrokes - The user draws on the canvas and the resulting stokes are sent to the server for relay to all the clients</li>
<li>OnInkStrokesUpdate - Another user has drawn and the server then uses the registered callbacks to update the canvas for all clients</li>
</ul>
<p><img src="DrawMe5.jpg" alt="The Drawing Process" width="800" height="430" /></p>
<p>All inkstrokes in DrawMe are sent as <code>MemoryStream</code> objects (or the underlying byte array representation). Note that we are not being very smart about how 
we are sending the strokes; we send the entire contents of the inkcanvas rather than the most recent update. For demonstration purposes this is ok since it makes the 
erasing code simple to handle (it's the same as the drawing code!). We have plans in place to optimise the sending of inkstrokes but unfortunately we couldn't implement this in time for this article. 
The following code listing show how we implemented this functionality in the client.
</p>
<pre lang="cs">  private void SaveGesture()
  {
      try
      {
          MemoryStream memoryStream = new MemoryStream();
  
          this.inkCanv.Strokes.Save(memoryStream);
             
          memoryStream.Flush();
  
          DrawMeServiceClient.Instance.SendInkStrokes(memoryStream);
      }
      catch (Exception exc)
      {
          MessageBox.Show(exc.Message, Title);
      }
  }
</pre>
<p>Once the strokes are sent to the server, the following code is executed to update all the registered clients. 
Notice how we call <code>GetBuffer()</code> on the memory stream passed in when we are sending the stroke updates back to each client. 
Initially we were just passing the <code>MemoryStream</code> object around, but we soon ran into problems with the object being garbage collected before we could use it. 
This is because each client needs to ensure that all user interface updates occur on the main GUI thread, and so we use an anonymous delegate to post an asynchronous call to the GUI thread. 
By the time the GUI thread processes the update, it's possible that the <code>MemoryStream</code> has already been garbage collected. 
This seems obvious now, but at the time it had us baffled for a few minutes!
</p>
<pre lang="cs">  public class DrawMeService : IDrawMeService
  {
      public void SendInkStrokes(MemoryStream memoryStream)
      {
          IDrawMeServiceCallback client = OperationContext.Current.GetCallbackChannel<IDrawMeServiceCallback>();
          
          foreach (IDrawMeServiceCallback callbackClient in s_dictCallbackToUser.Keys)
          {
              if (callbackClient != OperationContext.Current.GetCallbackChannel<IDrawMeServiceCallback>())
              {
                  callbackClient.OnInkStrokesUpdate(s_dictCallbackToUser[client], memoryStream.GetBuffer());
              }
          }
      }
  
      ...
  }
</pre>

<h3><a name="logoutseq">LogOff</a></h3>
<p>
When a user logs off, the application contacts the server and notifies it that the client should be removed from the registered clients list. 
If the user is also hosting the server then all clients are disconnected and returned to login mode. 
</p>
<p><img src="DrawMe6.jpg" alt="The Log-off Process" width="600" height="377" /></p>
<p>Here's the code that gets executed on the server when a client leaves.
</p>
<pre lang="cs">  public void Leave(ChatUser chatUser)
  {
      IDrawMeServiceCallback client = OperationContext.Current.GetCallbackChannel<IDrawMeServiceCallback>();
      if (s_dictCallbackToUser.ContainsKey(client))
      {
          s_dictCallbackToUser.Remove(client);
      }
  
      foreach (IDrawMeServiceCallback callbackClient in s_dictCallbackToUser.Keys)
      {
          if (chatUser.IsServer)
          {
              if (callbackClient != client)
              {
                  //server user logout, disconnect clients
                  callbackClient.ServerDisconnected();
              }
          }
          else
          {
              //normal user logout
              callbackClient.UpdateUsersList(s_dictCallbackToUser.Values.ToList());
          }
      }
  
      if (chatUser.IsServer)
      {
          s_dictCallbackToUser.Clear();
      }
  }
</pre>

<h2><a name="commun">Communicating using WCF</a></h2>
<p>
So far we haven't really talked too much about how we used WCF to implement the communication between instances of the DrawMe application. 
In this section we provide an overview of the key WCF features that we used. There were three main problems that we needed to solve to get the communiation working:
</p>

<ul>
<li>Serialize custom objects - Provide a means to send our class instance objects over the network</li>
<li>Define service contract - Specify an interface that the server will implement</li>
<li>Provide client callback functions - Specify a callback interface that the server can utilise to call methods on each client instance</li>
</ul>

<p>WCF provides solutions to all of these problems!
</p>

<h3><a name="serial">Serializing objects</a></h3>
    <p>Many in-built types in .NET are serializable by default. This means that they can be represented in a standard manner for passing over network connections. 
    However, when you define a new class it is not serializable by default. To store information about each DrawMe client user we created a <code>ChatUser</code> class. 
    In order to pass <code>ChatUser</code> objects over a network connection we need to specify that they are serializable.
    </p>
    
    <p> We have setup the <code>ChatUser</code> class to use the WCF <code>System.Runtime.Serialization</code> - <code>[DataContract]</code> attribute. 
    Applying this attribute to a class indicates that we are interested in serializing it. 
    To serialize a specfic member of the class we need to apply the <code>[DataMember]</code> attribute. 
    This is because data contracts have been designed with an "opt-in" programming model. i.e. Anything that is not explicitly marked with the <code>DataMember</code> attribute is not serialized.
    The following code snippet shows how we applied these attributes to the <code>ChatUser</code> class. See <code>ChatUser.cs</code> for the whole implementation.
    </p>
    
    <pre lang="cs">    [DataContract]
    public class ChatUser
    {
        ...
        
        [DataMember]
        public string NickName
        {
            get { return m_strNickName; }
            set { m_strNickName = value; }
        }
        
        ...		
    }
    </pre>

<h3><a name="contract">Service contract</a></h3>
<p>In order for each DrawMe client to be able to communicate with the server, a contract needs to be established. 
The purpose of the contract is to publish the interface that the server will implement so that clients know what methods are available on the server. 
In WCF, a contract can be specified by applying the <code>ServiceContract</code> attribute to an interface. 
When applying this attribute it is also possible to specify a <code>CallbackContract</code> which represents a callback interface that the client will implement.
You can see how we used the attribute in the following code.
</p>

<pre lang="cs">    [
       ServiceContract
       (
           Name = "DrawMeService",
           Namespace = "http://DrawMe/DrawMeService/",
           SessionMode = SessionMode.Required,
           CallbackContract = typeof(IDrawMeServiceCallback)
       )
    ]
    public interface IDrawMeService
    {
        [OperationContract()]
        bool Join(ChatUser chatUser);

        [OperationContract()]
        void Leave(ChatUser chatUser);

        [OperationContract()]
        bool IsUserNameTaken(string strUserName);

        [OperationContract()]
        void SendInkStrokes(MemoryStream memoryStream);
    }
</pre>

<p>Each client only needs to know about the <code>IDrawMeService</code> interface; however, the server needs to contain the implementation. 
When providing the implementation it is possible to specify a <code>ServiceBehavior</code> attribute. 
The DrawMe server uses the following service behaviour.
</p>

    <ul>
    <li>
        ConcurrencyMode - Single. The service will only handle one incoming call at
        a time.</li>
    <li>
        InstanceContextMode - Single.
        Only one <code>DrawMeService </code>
        object is used for all incoming calls and is not recycled subsequent to the calls.
        If the DrawMe
        service object does not exist, one is created. This is effectively a singleton.</li>
    </ul>
    
    <p>Here is how we have applied the <code>ServiceBehavior</code> attribute to the <code>DrawMeService</code> implementation.
    </p>
    <pre lang="cs">    [
        ServiceBehavior
        (
            ConcurrencyMode = ConcurrencyMode.Single, 
            InstanceContextMode = InstanceContextMode.Single
        )
    ]
    public class DrawMeService : IDrawMeService
    {
        ...
    }        
</pre>

<h3><a name="callbacks">Client callbacks</a></h3>
<p>
</p>
<p>
    DrawMe has a IDrawMeServiceCallback interface which allows the DrawMe server to send messages back to the client application. For example, when a new user has joined
    the chat room, the server uses the callback mechanism to notify all other users. 
    The callback interface is defined in a shared DrawMeInterfaces.dll; the implementation is located at the client side - see ClientCallBack.cs</p>
    <p>
    DrawMe clients implement three callback functions:</p>
    <ul>
    <li>UpdateUsersList - DrawMe server notifies all active clients when a new user has
        joined the chat room</li>
        <li>OnInkStrokesUpdate - For the DrawMe server to send the latest ink strokes to all active clients</li>
        <li>ServerDisconnected - This function is used to notify all
            active clients when the server disconnects</li>
    </ul>
    <p>
        It's possible to specify an <code>OperationContract</code> attribute on each callback function. 
        In DrawMe, we have chosen to implement the callbacks with the <code>IsOneWay=true</code> attribute, 
        i.e. The operations don't relay any information back to the server about whether or not they were successful.
    </p>
<pre lang="cs">    public interface IDrawMeServiceCallback
    {
        [OperationContract(IsOneWay = true)]
        void UpdateUsersList(List<ChatUser> listChatUsers);

        [OperationContract(IsOneWay = true)]
        void OnInkStrokesUpdate(ChatUser chatUser, byte[] bytesStroke);

        [OperationContract(IsOneWay = true)]
        void ServerDisconnected();
    }
</pre>

<h2><a name="conclusion">Conclusion</a></h2>
<p>This article has hopefully given you an overview of some of the available features in WCF. 
In terms of meeting our goal of implementing a collaborative drawing program, we've demonstrated that this is not only possible but relatively easy to do using some of the cool new WCF functionality. 
Actually, we think we probably spent more time writing this acticle than we did writing the code for it, so that should give you some indication of how powerful the WCF framework is (assuming we're not terrible writers!).
Please feel free to download the source code and delve into the structure, and leave you comments or questions below!
</p>

<h2><a name="appendix">Appendix - Collaborating via CodePlex</a></h2>
<p>During the design and coding stages of this small project we wanted a way to collaborate without having to keep going to each other's house every time we wanted to work on the project. Using a web-based free source control system was the 
obvious solution. We decided to try out CodePlex (<a href="http://www.codeplex.com/">http://www.codeplex.com/</a>) which is Microsoft's open source project hosting web site. 
We found CodePlex to be a very useful tool in terms of coordinating the work effort and keeping track of what still needed to be implemented. What's more, CodePlex has a very intuitive user interface and neither of us had any difficulty in using it. 
</p>
<p>
The backend of CodePlex uses a system of Team Foundation Server (TFS) databases to store all the community projects on. 
Given that VS2008 integrates tightly with TFS we originally planned to use <a href="http://www.microsoft.com/downloads/details.aspx?familyid=0ed12659-3d41-4420-bbb0-a46e51bfca86&displaylang=en">Team Explore 2008</a> as the 
source control client. Team Explore 2008 is a free, simplified TFS client from Microsoft that can integrate with the VS2008 development environment directly. 
Unfortunately Team Explore 2008 doesn't work with VS2008 Beta 2 (we found this out the hard way after downloading 387Mb). But in the end it didn't really matter because we 
were able to use <a href="http://tortoisesvn.tigris.org/">TortoiseSVN</a> (a subversion client for windows) to access the TFS that our project was stored on. Information on how to do this is readily 
available in the CodePlex FAQ.
</p>
<p>Once we got the source control access sorted out it was very easy for us to collaborate on the project. The thing we really liked about CodePlex was the integrated Issue Tracker; 
raising issues was painless and easy, as was assigning issues to each other for work. In summary, if you're thinking about starting up an open-source project with more 
than one developer then using CodePlex is definitely an option worth exploring.
</p>
<p>If you are interested in &quot;checking out&quot; the DrawMe project on CodePlex head on over to <a href="http://www.codeplex.com/drawme">http://www.codeplex.com/drawme</a> and take a look around.
The &quot;Issue Tracker&quot; and &quot;Source Code&quot; tabs are probably the most interesting in terms of seeing the workflow process we went through.
</p>

</body>
</html>